package com.dongzili.common.config.aspect;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dongzili.common.config.exception.GlobalExceptionHandler;
import com.dongzili.common.disruptor.entity.MessageEvent;
import com.dongzili.common.disruptor.enums.MessageType;
import com.dongzili.common.disruptor.service.DisruptorMqService;
import com.dongzili.common.utils.IpUtils;
import com.dongzili.common.vo.R;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Resource
    private TraceIdLogger messageLogger;


//    @Resource
//    private HttpServletRequest request;
//    @Resource
//    private HttpServletResponse response;
    @Resource
    private DisruptorMqService<LogPrintInfo> disruptorMqService;

    @Pointcut("execution(* com..*Controller.*(..))")
    public void pointcut() {}

    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();
        LocalDateTime start = LocalDateTime.now();

        if (messageLogger.getTraceId() == null) {
            messageLogger.setTraceId(UUID.randomUUID().toString());
        }
        String traceId = messageLogger.getTraceId();
        log.info("{} - 会话追踪ID", traceId);

        Object result = null;
        Throwable throwable = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            throwable = e;
//            HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
            result = GlobalExceptionHandler.generalHandleException(new RuntimeException(e), request, messageLogger);
        } finally {
            LocalDateTime end = LocalDateTime.now();
            long duration = LocalDateTimeUtil.between(start, end, ChronoUnit.MILLIS);
            try{
                if (result instanceof R<?> apiResponse) {
                    apiResponse.setTraceId(traceId);
                    apiResponse.setTimes(duration);
                    if (apiResponse.getData() instanceof List<?>) {
                        int size = ((List<?>) apiResponse.getData()).size();
                        apiResponse.setCount((long) size);
                    } else if (apiResponse.getData() instanceof Page<?> page) {
                        long size = page.getTotal();
                        apiResponse.setCount(size);
                    }
                }
            }catch (Exception ignore){}
            // 异步记录日志
            String url = request.getRequestURL().toString();
            String method = request.getMethod();
            String uri = request.getRequestURI();
            String queryString = request.getQueryString();
            String ip = IpUtils.getClientIP(request);
            RequestInfo requestInfo = new RequestInfo();
            requestInfo.setUrl(url);
            requestInfo.setMethod(method);
            requestInfo.setUri(uri);
            requestInfo.setQueryString(queryString);
            requestInfo.setIp(ip);
            requestInfo.setContentType(request.getContentType());
            LogPrintInfo printInfo = new LogPrintInfo();
            printInfo.setTraceId(traceId);
            printInfo.setArgs(JSONUtil.toJsonStr(joinPoint.getArgs(), JSONConfig.create().setIgnoreNullValue(true)));
            printInfo.setResult(result);
            printInfo.setDuration(duration);
            printInfo.setRequest(requestInfo);
            printInfo.setErrMsg(throwable==null?null:throwable.getLocalizedMessage());

            MessageEvent<LogPrintInfo> event = new MessageEvent<>();
            event.setData(printInfo);
            disruptorMqService.sendMq(MessageType.Log, event);
//                asyncLogService.logRequest(printInfo);

        }

        return result;
    }
}